home *** CD-ROM | disk | FTP | other *** search
/ Reverse Code Engineering RCE CD +sandman 2000 / ReverseCodeEngineeringRceCdsandman2000.iso / RCE / Ebooks / Thinking in C++ V2 / C26 / ExtractCode.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  2000-05-25  |  22.6 KB  |  755 lines

  1. //: C26:ExtractCode.cpp
  2. // From Thinking in C++, 2nd Edition
  3. // Available at http://www.BruceEckel.com
  4. // (c) Bruce Eckel 1999
  5. // Copyright notice in Copyright.txt
  6. // Automatically extracts code files from
  7. // ASCII text of this book.
  8. #include <iostream>
  9. #include <fstream>
  10. #include <string>
  11. #include <vector>
  12. #include <map>
  13. #include <set>
  14. #include <algorithm>
  15. using namespace std;
  16.  
  17. string copyright =
  18.   "// From Thinking in C++, 2nd Edition\n"
  19.   "// Available at http://www.BruceEckel.com\n"
  20.   "// (c) Bruce Eckel 1999\n"
  21.   "// Copyright notice in Copyright.txt\n";
  22.  
  23. string usage =
  24.   " Usage:ExtractCode source\n"
  25.   "where source is the ASCII file containing \n"
  26.   "the embedded tagged sourcefiles. The ASCII \n"
  27.   "file must also contain an embedded compiler\n"
  28.   "configuration file called CompileDB.txt \n"
  29.   "See Thinking in C++, 2nd ed. for details\n";
  30.  
  31. // Tool to remove the white space from both ends:
  32. string trim(const string& s) {
  33.   if(s.length() == 0)
  34.     return s;
  35.   int b = s.find_first_not_of(" \t");
  36.   int e = s.find_last_not_of(" \t");
  37.   if(b == -1) // No non-spaces
  38.     return "";
  39.   return string(s, b, e - b + 1);
  40. }
  41.  
  42. // Manage all the error messaging:
  43. void error(string problem, string message) {
  44.   static const string border(
  45.   "-----------------------------------------\n");
  46.   class ErrReport {
  47.     int count;
  48.     string fname;
  49.   public:
  50.     ofstream errs;
  51.     ErrReport(char* fn = "ExtractCodeErrors.txt") 
  52.       : count(0),fname(fn),errs(fname.c_str()) {}
  53.     void operator++(int) { count++; }
  54.     ~ErrReport() {
  55.       errs.close();
  56.       // Dump error messages to console
  57.       ifstream in(fname.c_str());
  58.       cerr << in.rdbuf() << endl;
  59.       cerr << count << " Errors found" << endl;
  60.       cerr << "Messages in " << fname << endl;
  61.     }
  62.   };
  63.   // Created on first call to this function;
  64.   // Destructor reports total errors:
  65.   static ErrReport report;
  66.   report++;
  67.   report.errs << border << message << endl
  68.     << "Problem spot: " << problem << endl;
  69. }
  70.  
  71. ///// OS-specific code, hidden inside a class:
  72. #ifdef __GNUC__  // For egcs under Linux/Unix
  73. #include <unistd.h>
  74. #include <sys/stat.h>
  75. #include <stdlib.h>
  76. class OSDirControl {
  77. public:
  78.   static string getCurrentDir() {
  79.     char path[PATH_MAX];
  80.     getcwd(path, PATH_MAX);
  81.     return string(path);
  82.   }
  83.   static void makeDir(string dir) {
  84.     mkdir(dir.c_str(), 0777);
  85.   }
  86.   static void changeDir(string dir) {
  87.     chdir(dir.c_str());
  88.   }
  89. };
  90. #else // For Dos/Windows:
  91. #include <direct.h>
  92. class OSDirControl {
  93. public:
  94.   static string getCurrentDir() {
  95.     char path[_MAX_PATH];
  96.     getcwd(path, _MAX_PATH);
  97.     return string(path);
  98.   }
  99.   static void makeDir(string dir) {
  100.     mkdir(dir.c_str());
  101.   }
  102.   static void changeDir(string dir) {
  103.     chdir(dir.c_str());
  104.   }
  105. };
  106. #endif ///// End of OS-specific code
  107.  
  108. class PushDirectory {
  109.   string oldpath;
  110. public:
  111.   PushDirectory(string path);
  112.   ~PushDirectory() {
  113.     OSDirControl::changeDir(oldpath);
  114.   }
  115.   void pushOneDir(string dir) {
  116.     OSDirControl::makeDir(dir);
  117.     OSDirControl::changeDir(dir);
  118.   }
  119. };
  120.  
  121. PushDirectory::PushDirectory(string path) {
  122.   oldpath = OSDirControl::getCurrentDir();
  123.   while(path.length() != 0) {
  124.     int colon = path.find(':');
  125.     if(colon != string::npos) {
  126.       pushOneDir(path.substr(0, colon));
  127.       path = path.substr(colon + 1);
  128.     } else {
  129.       pushOneDir(path);
  130.       return;
  131.     }
  132.   }
  133. }
  134.  
  135. //--------------- Manage code files -------------
  136. // A CodeFile object knows everything about a
  137. // particular code file, including contents, path
  138. // information, how to compile, link, and test 
  139. // it, and which compilers it won't compile with.
  140. enum TType {header, object, executable, none};
  141.  
  142. class CodeFile {
  143.   TType _targetType;
  144.   string _rawName, // Original name from input
  145.     _path, // Where the source file lives
  146.     _file, // Name of the source file
  147.     _base, // Name without extension
  148.     _tname, // Target name
  149.     _testArgs; // Command-line arguments
  150.   vector<string>
  151.     lines, // Contains the file
  152.     _compile, // Compile dependencies
  153.     _link; // How to link the executable
  154.   set<string>
  155.     _noBuild; // Compilers it won't compile with
  156.   bool writeTags; // Whether to write the markers
  157.   // Initial makefile processing for the file:
  158.   void target(const string& s);
  159.   // For quoted #include headers:
  160.   void headerLine(const string& s);
  161.   // For special dependency tag marks:
  162.   void dependLine(const string& s);
  163. public:
  164.   CodeFile(istream& in, string& s);
  165.   const string& rawName() { return _rawName; }
  166.   const string& path() { return _path; }
  167.   const string& file() { return _file; }
  168.   const string& base() { return _base; }
  169.   const string& targetName() { return _tname; }
  170.   TType targetType() { return _targetType; }
  171.   const vector<string>& compile() {
  172.     return _compile;
  173.   }
  174.   const vector<string>& link() {
  175.     return _link;
  176.   }
  177.   const set<string>& noBuild() {
  178.     return _noBuild;
  179.   }
  180.   const string& testArgs() { return _testArgs; }
  181.   // Add a compiler it won't compile with:
  182.   void addFailure(const string& failure) {
  183.     _noBuild.insert(failure);
  184.   }
  185.   bool compilesOK(string compiler) {
  186.     return _noBuild.count(compiler) == 0;
  187.   }
  188.   friend ostream&
  189.   operator<<(ostream& os, const CodeFile& cf) {
  190.     copy(cf.lines.begin(), cf.lines.end(),
  191.       ostream_iterator<string>(os, ""));
  192.     return os;
  193.   }
  194.   void write() {
  195.     PushDirectory pd(_path);
  196.     ofstream listing(_file.c_str());
  197.     listing << *this;  // Write the file
  198.   }
  199.   void dumpInfo(ostream& os);
  200. };
  201.  
  202. void CodeFile::target(const string& s) {
  203.   // Find the base name of the file (without
  204.   // the extension):
  205.   int lastDot = _file.find_last_of('.');
  206.   if(lastDot == string::npos) {
  207.     error(s, "Missing extension");
  208.     exit(1);
  209.   }
  210.   _base = _file.substr(0, lastDot);
  211.   // Determine the type of file and target:
  212.   if(s.find(".h") != string::npos ||
  213.      s.find(".H") != string::npos) {
  214.     _targetType = header;
  215.     _tname = _file;
  216.     return;
  217.   }
  218.   if(s.find(".txt") != string::npos
  219.       || s.find(".TXT") != string::npos
  220.       || s.find(".dat") != string::npos
  221.       || s.find(".DAT") != string::npos) {
  222.     // Text file, not involved in make
  223.     _targetType = none;
  224.     _tname = _file;
  225.     return;
  226.   }
  227.   // C++ objs/exes depend on their own source:
  228.   _compile.push_back(_file);
  229.   if(s.find("{O}") != string::npos) {
  230.     // Don't build an executable from this file
  231.     _targetType = object;
  232.     _tname = _base;
  233.   } else {
  234.     _targetType = executable;
  235.     _tname = _base;
  236.     // The exe depends on its own object file:
  237.     _link.push_back(_base);
  238.   }
  239. }
  240.  
  241. void CodeFile::headerLine(const string& s) {
  242.   int start = s.find('\"');
  243.   int end = s.find('\"', start + 1);
  244.   int len = end - start - 1;
  245.   _compile.push_back(s.substr(start + 1, len));
  246. }
  247.  
  248. void CodeFile::dependLine(const string& s) {
  249.   const string linktag("//{L} ");
  250.   string deps = trim(s.substr(linktag.length()));
  251.   while(true) {
  252.     int end = deps.find(' ');
  253.     string dep = deps.substr(0, end);
  254.     _link.push_back(dep);
  255.     if(end == string::npos) // Last one
  256.       break;
  257.     else
  258.       deps = trim(deps.substr(end));
  259.   }
  260. }
  261.  
  262. CodeFile::CodeFile(istream& in, string& s) {
  263.   // If false, don't write begin & end tags:
  264.   writeTags = (s[3] != '!');
  265.   // Assume a space after the starting tag:
  266.   _file = s.substr(s.find(' ') + 1);
  267.   // There will always be at least one colon:
  268.   int lastColon = _file.find_last_of(':');
  269.   if(lastColon == string::npos) {
  270.     error(s, "Missing path");
  271.     lastColon = 0; // Recover from error
  272.   }
  273.   _rawName = trim(_file);
  274.   _path = _file.substr(0, lastColon);
  275.   _file = _file.substr(lastColon + 1);
  276.   _file =_file.substr(0,_file.find_last_of(' '));
  277.   cout << "path = [" << _path << "] "
  278.     << "file = [" << _file << "]" << endl;
  279.   target(s); // Determine target type
  280.   if(writeTags){
  281.     lines.push_back(s + '\n');
  282.     lines.push_back(copyright);
  283.   }
  284.   string s2;
  285.   while(getline(in, s2)) {
  286.     // Look for specified link dependencies:
  287.     if(s2.find("//{L}") == 0) // 0: Start of line
  288.       dependLine(s2);
  289.     // Look for command-line arguments for test:
  290.     if(s2.find("//{T}") == 0) // 0: Start of line
  291.       _testArgs = s2.substr(strlen("//{T}") + 1);
  292.     // Look for quoted includes:
  293.     if(s2.find("#include \"") != string::npos) {
  294.       headerLine(s2); // Grab makefile info
  295.     }
  296.     // Look for end marker:
  297.     if(s2.find("//" "/:~") != string::npos) {
  298.       if(writeTags)
  299.         lines.push_back(s2 + '\n');
  300.       return;  // Found the end
  301.     }
  302.     // Make sure you don't see another start:
  303.     if(s2.find("//" ":") != string::npos
  304.        || s2.find("/*" ":") != string::npos) {
  305.       error(s, "Error: new file started before"
  306.         " previous file concluded");
  307.       return;
  308.     }
  309.     // Write ordinary line:
  310.     lines.push_back(s2 + '\n');
  311.   }
  312. }
  313.  
  314. void CodeFile::dumpInfo(ostream& os) {
  315.   os << _path << ':' << _file << endl;
  316.   os << "target: " << _tname << endl;
  317.   os << "compile: " << endl;
  318.   for(int i = 0; i < _compile.size(); i++)
  319.     os << '\t' << _compile[i] << endl;
  320.   os << "link: " << endl;
  321.   for(int i = 0; i < _link.size(); i++)
  322.     os << '\t' << _link[i] << endl;
  323.   if(_noBuild.size() != 0) {
  324.     os << "Won't build with: " << endl;
  325.     copy(_noBuild.begin(), _noBuild.end(),
  326.       ostream_iterator<string>(os, "\n"));
  327.   }
  328. }
  329.  
  330. //--------- Manage compiler information ---------
  331. class CompilerData {
  332.   // Information about each compiler:
  333.   vector<string> rules; // Makefile rules
  334.   set<string> fails; // Non-compiling files
  335.   string objExtension; // File name extensions
  336.   string exeExtension;
  337.   // For OS-specific activities:
  338.   bool _dos, _unix;
  339.   // Store the information for all the compilers:
  340.   static map<string, CompilerData> compilerInfo;
  341.   static set<string> _compilerNames;
  342. public:
  343.   CompilerData() : _dos(false), _unix(false) {}
  344.   // Read database of various compiler's 
  345.   // information and failure listings for 
  346.   // compiling the book files:
  347.   static void readDB(istream& in);
  348.   // For enumerating all the compiler names:
  349.   static set<string>& compilerNames() {
  350.     return _compilerNames;
  351.   }
  352.   // Tell this CodeFile which compilers
  353.   // don't work with it:
  354.   static void addFailures(CodeFile& cf);
  355.   // Produce the proper object file name
  356.   // extension for this compiler:
  357.   static string obj(string compiler);
  358.   // Produce the proper executable file name
  359.   // extension for this compiler:
  360.   static string exe(string compiler);
  361.   // For inserting a particular compiler's
  362.   // rules into a makefile:
  363.   static void 
  364.   writeRules(string compiler, ostream& os);
  365.   // Change forward slashes to backward 
  366.   // slashes if necessary:
  367.   static string 
  368.   adjustPath(string compiler, string path);
  369.   // So you can ask if it's a Unix compiler:
  370.   static bool isUnix(string compiler) {
  371.     return compilerInfo[compiler]._unix;
  372.   }
  373.   // So you can ask if it's a dos compiler:
  374.   static bool isDos(string compiler) {
  375.     return compilerInfo[compiler]._dos;
  376.   }
  377.   // Display information (for debugging):
  378.   static void dump(ostream& os = cout);
  379. };
  380.  
  381. // Static initialization:
  382. map<string,CompilerData> 
  383.   CompilerData::compilerInfo;
  384. set<string> CompilerData::_compilerNames;
  385.  
  386. void CompilerData::readDB(istream& in) {
  387.   string compiler; // Name of current compiler
  388.   string s;
  389.   while(getline(in, s)) {
  390.     if(s.find("#//" "/:~") == 0)
  391.       return; // Found end tag
  392.     s = trim(s);
  393.     if(s.length() == 0) continue; // Blank line
  394.     if(s[0] == '#') continue; // Comment
  395.     if(s[0] == '{') { // Different compiler
  396.       compiler = s.substr(0, s.find('}'));
  397.       compiler = trim(compiler.substr(1));
  398.       if(compiler.length() != 0)
  399.         _compilerNames.insert(compiler);
  400.       continue; // Changed compiler name
  401.     }
  402.     if(s[0] == '(') { // Object file extension
  403.       string obj = s.substr(1);
  404.       obj = trim(obj.substr(0, obj.find(')')));
  405.       compilerInfo[compiler].objExtension =obj;
  406.       continue;
  407.     }
  408.     if(s[0] == '[') { // Executable extension
  409.       string exe = s.substr(1);
  410.       exe = trim(exe.substr(0, exe.find(']')));
  411.       compilerInfo[compiler].exeExtension =exe;
  412.       continue;
  413.     }
  414.     if(s[0] == '&') { // Special directive
  415.       if(s.find("dos") != string::npos)
  416.         compilerInfo[compiler]._dos = true;
  417.       else if(s.find("unix") != string::npos)
  418.         compilerInfo[compiler]._unix = true;
  419.       else
  420.         error("Compiler Information Database", 
  421.           "unknown special directive: " + s);
  422.       continue;
  423.     }
  424.     if(s[0] == '@') { // Makefile rule
  425.       string rule(s.substr(1)); // Remove the @
  426.       if(rule[0] == ' ') // Space means tab
  427.         rule = '\t' + trim(rule);
  428.       compilerInfo[compiler].rules
  429.         .push_back(rule); 
  430.       continue;
  431.     }
  432.     // Otherwise, it's a failure line:
  433.     compilerInfo[compiler].fails.insert(s);
  434.   }
  435.   error("CompileDB.txt","Missing end tag");
  436. }
  437.  
  438. void CompilerData::addFailures(CodeFile& cf) {
  439.   set<string>::iterator it = 
  440.     _compilerNames.begin();
  441.   while(it != _compilerNames.end()) {
  442.     if(compilerInfo[*it]
  443.        .fails.count(cf.rawName()) != 0)
  444.       cf.addFailure(*it);
  445.     it++;
  446.   }
  447. }
  448.  
  449. string CompilerData::obj(string compiler) {
  450.   if(compilerInfo.count(compiler) != 0) {
  451.     string ext(
  452.       compilerInfo[compiler].objExtension);
  453.     if(ext.length() != 0)
  454.       ext = '.' + ext; // Use '.' if it exists
  455.     return ext;
  456.   } else
  457.     return "No such compiler information";
  458. }
  459.  
  460. string CompilerData::exe(string compiler) {
  461.   if(compilerInfo.count(compiler) != 0) {
  462.     string ext(
  463.       compilerInfo[compiler].exeExtension);
  464.     if(ext.length() != 0)
  465.       ext = '.' + ext; // Use '.' if it exists
  466.     return ext;
  467.   } else
  468.     return "No such compiler information";
  469. }
  470.  
  471. void CompilerData::writeRules(
  472.   string compiler, ostream& os) {
  473.   if(_compilerNames.count(compiler) == 0) {
  474.     os << "No info on this compiler" << endl;
  475.     return;
  476.   }
  477.   vector<string>& r = 
  478.     compilerInfo[compiler].rules;
  479.   copy(r.begin(), r.end(), 
  480.     ostream_iterator<string>(os, "\n"));
  481. }
  482.  
  483. string CompilerData::adjustPath(
  484.   string compiler, string path) {
  485.   // Use STL replace() algorithm:
  486.   if(compilerInfo[compiler]._dos)
  487.     replace(path.begin(), path.end(), '/', '\\');
  488.   return path;
  489. }
  490.  
  491. void CompilerData::dump(ostream& os) {
  492.   ostream_iterator<string> out(os, "\n");
  493.   *out++ = "Compiler Names:";
  494.   copy(_compilerNames.begin(), 
  495.     _compilerNames.end(), out);
  496.   map<string, CompilerData>::iterator compIt;
  497.   for(compIt = compilerInfo.begin(); 
  498.     compIt != compilerInfo.end(); compIt++) {
  499.     os << "******************************\n";
  500.     os << "Compiler: [" << (*compIt).first <<
  501.       "]" << endl;
  502.     CompilerData& cd = (*compIt).second;
  503.     os << "objExtension: " << cd.objExtension
  504.       << "\nexeExtension: " << cd.exeExtension 
  505.       << endl;
  506.     *out++ = "Rules:";
  507.     copy(cd.rules.begin(), cd.rules.end(), out);
  508.     cout << "Won't compile with: " << endl;
  509.     copy(cd.fails.begin(), cd.fails.end(), out);
  510.   }
  511. }
  512.  
  513. // ---------- Manage makefile creation ----------
  514. // Create the makefile for this directory, based
  515. // on each of the CodeFile entries:
  516. class Makefile {
  517.   vector<CodeFile> codeFiles;
  518.   // All the different paths 
  519.   // (for creating the Master makefile):
  520.   static set<string> paths;
  521.   void 
  522.   createMakefile(string compiler, string path);
  523. public:
  524.   Makefile() {}
  525.   void addEntry(CodeFile& cf) {
  526.     paths.insert(cf.path()); // Record all paths
  527.     // Tell it what compilers don't work with it:
  528.     CompilerData::addFailures(cf);
  529.     codeFiles.push_back(cf);
  530.   }
  531.   // Write the makefile for each compiler:
  532.   void writeMakefiles(string path);
  533.   // Create the master makefile:
  534.   static void writeMaster(string flag = "");
  535. };
  536.  
  537. // Static initialization:
  538. set<string> Makefile::paths;
  539.  
  540. void Makefile::writeMakefiles(string path) {
  541.   if(trim(path).length() == 0)
  542.     return; // No makefiles in root directory
  543.   PushDirectory pd(path);
  544.   set<string>& compilers = 
  545.     CompilerData::compilerNames();
  546.   set<string>::iterator it = compilers.begin();
  547.   while(it != compilers.end())
  548.     createMakefile(*it++, path);
  549. }
  550.  
  551. void Makefile::createMakefile(
  552.   string compiler, string path) {
  553.   string // File name extensions:
  554.     exe(CompilerData::exe(compiler)),
  555.     obj(CompilerData::obj(compiler));
  556.   string filename(compiler + ".makefile");
  557.   ofstream makefile(filename.c_str());
  558.   makefile << 
  559.     "# From Thinking in C++, 2nd Edition\n"
  560.     "# At http://www.BruceEckel.com\n"
  561.     "# (c) Bruce Eckel 1999\n"
  562.     "# Copyright notice in Copyright.txt\n"
  563.     "# Automatically-generated MAKEFILE \n"
  564.     "# For examples in directory "+ path + "\n"
  565.     "# using the " + compiler + " compiler\n"
  566.     "# Note: does not make files that will \n"
  567.     "# not compile with this compiler\n"
  568.     "# Invoke with: make -f " 
  569.     + compiler + ".makefile\n"
  570.     << endl;
  571.   CompilerData::writeRules(compiler, makefile);
  572.   vector<string> makeAll, makeTest, 
  573.     makeBugs, makeDeps, linkCmd;
  574.   // Write the "all" dependencies:
  575.   makeAll.push_back("all: ");
  576.   makeTest.push_back("test: all ");
  577.   makeBugs.push_back("bugs: ");
  578.   string line;
  579.   vector<CodeFile>::iterator it;
  580.   for(it = codeFiles.begin(); 
  581.     it != codeFiles.end(); it++) {
  582.     CodeFile& cf = *it;
  583.     if(cf.targetType() == executable) {
  584.       line = "\\\n\t"+cf.targetName()+ exe + ' ';
  585.       if(cf.compilesOK(compiler) == false) {
  586.         makeBugs.push_back(
  587.           CompilerData::adjustPath(
  588.             compiler,line));
  589.       } else {
  590.         makeAll.push_back(
  591.           CompilerData::adjustPath(
  592.             compiler,line));
  593.         line = "\\\n\t" + cf.targetName() + exe +
  594.           ' ' + cf.testArgs() + ' ';
  595.         makeTest.push_back(
  596.           CompilerData::adjustPath(
  597.             compiler,line));
  598.       }
  599.       // Create the link command:
  600.       int linkdeps = cf.link().size();
  601.       string linklist;
  602.       for(int i = 0; i < linkdeps; i++)
  603.         linklist += 
  604.           cf.link().operator[](i) + obj + " ";
  605.       line = cf.targetName() + exe + ": "
  606.         + linklist + "\n\t$(CPP) $(OFLAG)"
  607.         + cf.targetName() + exe
  608.         + ' ' + linklist + "\n\n";
  609.       linkCmd.push_back(
  610.         CompilerData::adjustPath(compiler,line));
  611.     }
  612.     // Create dependencies
  613.     if(cf.targetType() == executable
  614.       || cf.targetType() == object) {
  615.       int compiledeps = cf.compile().size();
  616.       string objlist(cf.base() + obj + ": ");
  617.       for(int i = 0; i < compiledeps; i++)
  618.         objlist += 
  619.           cf.compile().operator[](i) + " ";
  620.       makeDeps.push_back(
  621.         CompilerData::adjustPath(
  622.           compiler, objlist) +"\n");
  623.     }      
  624.   }
  625.   ostream_iterator<string> mkos(makefile, "");
  626.   *mkos++ = "\n";
  627.   // The "all" target:
  628.   copy(makeAll.begin(), makeAll.end(), mkos);
  629.   *mkos++ = "\n\n";
  630.   // Remove continuation marks from makeTest:
  631.   vector<string>::iterator si = makeTest.begin();
  632.   int bsl;
  633.   for(; si != makeTest.end(); si++)
  634.     if((bsl= (*si).find("\\\n")) != string::npos)
  635.       (*si).erase(bsl, strlen("\\"));
  636.   // Now print the "test" target:
  637.   copy(makeTest.begin(), makeTest.end(), mkos);
  638.   *mkos++ = "\n\n";
  639.   // The "bugs" target:
  640.   copy(makeBugs.begin(), makeBugs.end(), mkos);
  641.   if(makeBugs.size() == 1)
  642.     *mkos++ = "\n\t@echo No compiler bugs in "
  643.       "this directory!";
  644.   *mkos++ = "\n\n";
  645.   // Link commands:
  646.   copy(linkCmd.begin(), linkCmd.end(), mkos);
  647.   *mkos++ = "\n";
  648.   // Demendencies:
  649.   copy(makeDeps.begin(), makeDeps.end(), mkos);
  650.   *mkos++ = "\n";
  651. }
  652.  
  653. void Makefile::writeMaster(string flag) {
  654.   string filename = "makefile";
  655.   if(flag.length() != 0)
  656.     filename += '.' + flag;
  657.   ofstream makefile(filename.c_str());
  658.   makefile << "# Master makefile for "
  659.     "Thinking in C++, 2nd Ed. by Bruce Eckel\n"
  660.     "# at http://www.BruceEckel.com\n"
  661.     "# Compiles all the code in the book\n"
  662.     "# Copyright notice in Copyright.txt\n\n"
  663.     "help: \n"
  664.     "\t@echo To compile all programs from \n"
  665.     "\t@echo Thinking in C++, 2nd Ed., type\n"
  666.     "\t@echo one of the following commands,\n"
  667.     "\t@echo according to your compiler:\n";
  668.   set<string>& n = CompilerData::compilerNames();
  669.   set<string>::iterator nit;
  670.   for(nit = n.begin(); nit != n.end(); nit++)
  671.     makefile << 
  672.       string("\t@echo make " + *nit + "\n");
  673.   makefile << endl;
  674.   // Make for each compiler:
  675.   for(nit = n.begin(); nit != n.end(); nit++) {
  676.     makefile << *nit << ":\n";
  677.     for(set<string>::iterator it = paths.begin();
  678.       it != paths.end(); it++) {
  679.       // Ignore the root directory:
  680.       if((*it).length() == 0) continue;
  681.       makefile << "\tcd " << *it;
  682.       // Different commands for unix vs. dos:
  683.       if(CompilerData::isUnix(*nit))
  684.         makefile << "; ";
  685.       else
  686.         makefile << "\n\t";
  687.       makefile << "make -f " << *nit 
  688.         << ".makefile";
  689.       if(flag.length() != 0) {
  690.         makefile << ' ';
  691.         if(flag == "bugs")
  692.           makefile << "-i ";
  693.         makefile << flag;
  694.       }
  695.       makefile << "\n";
  696.       if(CompilerData::isUnix(*nit) == false)
  697.         makefile << "\tcd ..\n";
  698.     }
  699.     makefile << endl;
  700.   }
  701. }
  702.  
  703. int main(int argc, char* argv[]) {
  704.   if(argc < 2) {
  705.     error("Command line error", usage);
  706.     exit(1);
  707.   }
  708.   // For development & testing, leave off notice:
  709.   if(argc == 3)
  710.     if(string(argv[2]) == "-nocopyright")
  711.       copyright = "";
  712.   // Open the input file to read the compiler
  713.   // information database:
  714.   ifstream in(argv[1]);
  715.   if(!in) {
  716.     error(string("can't open ") + argv[1],usage);
  717.     exit(1);
  718.   }
  719.   string s;
  720.   while(getline(in, s)) {
  721.     // Break up the strings to prevent a match when
  722.     // this code is seen by this program:
  723.     if(s.find("#:" " :CompileDB.txt") 
  724.       != string::npos) {
  725.       // Parse the compiler information database:
  726.       CompilerData::readDB(in);
  727.       break; // Out of while loop
  728.     }
  729.   }
  730.   if(in.eof())
  731.     error("CompileDB.txt", "Can't find data");
  732.   in.seekg(0, ios::beg); // Back to beginning
  733.   map<string, Makefile> makeFiles;
  734.   while(getline(in, s)) {
  735.     // Look for tag at beginning of line:
  736.     if(s.find("//" ":") == 0
  737.        || s.find("/*" ":") == 0
  738.        || s.find("#" ":") == 0) {
  739.       CodeFile cf(in, s);
  740.       cf.write();  // Tell it to write itself
  741.       makeFiles[cf.path()].addEntry(cf);
  742.     }
  743.   }
  744.   // Write all the makefiles, telling each
  745.   // the path where it belongs:
  746.   map<string, Makefile>::iterator mfi;
  747.   for(mfi = makeFiles.begin(); 
  748.     mfi != makeFiles.end(); mfi++)
  749.     (*mfi).second.writeMakefiles((*mfi).first);
  750.   // Create the master makefile:
  751.   Makefile::writeMaster();
  752.   // Write the makefile that tries the bug files:
  753.   Makefile::writeMaster("bugs");
  754. } ///:~
  755.